/* ===================

AX2E (AXESCON XACML 2.0 Engine) is the Java authorization engine, which implements OASIS XACML 2.0 standard.
Copyright (C) 2007 AXESCON LLC

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA


Contact: AXESCON LLC - info{at}axescon.com

 =================== */
package axescon.xacml.engine;

import axescon.xacml.api.*;
import axescon.xacml.model.ctx.XacmlCtx;
import axescon.xacml.model.policy.AttributeValue;
import axescon.xacml.model.policy.Expression;
import axescon.xacml.model.shared.attr.*;

import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author Date: Sep 28, 2005
 *         Responsibilities:
 */
public class BaseFnFactory implements FnFactory {


    public Liquid exec(String fnName, Expression[] args,
                       CtxHandler ch, XacmlCtx req, ExpressionEvaluator ee) throws XacmlException, ExpressionEvalIndeterminateException {
        return getFn(fnName).exec(args, ch, req, ee);
    }

    /**
     * Map<String, Fn>
     */
    private Map fnMap = new TreeMap();
    /**
     * Map<String, MatchFn>
     */
    private Map matchFnMap = new TreeMap();

    public BaseFnFactory() throws XacmlException {

        this(false);
    }

    Logger log = Logger.getLogger(this.getClass().getName());

    public BaseFnFactory(boolean setDefaults) throws XacmlException {
        if (setDefaults) setDefaults();
    }

    Fn loadInstance(ClassLoader cl, String className) throws XacmlException {
        try {
            return (Fn) (cl.loadClass(className)).newInstance();
        } catch (Exception x) {
            if (log.isLoggable(Level.SEVERE)) {
                log.severe("BaseFnFactory cant load function class: " + className);
            }
            throw new XacmlException("BaseFnFactory cant load function class: " + className, x
                    , XacmlException.ERR_CODE_CLASS_LOAD);
        }

    }

    public void addFn(String urn, Fn fn) {
        fn.setFnFactory(this);
        fn.setFnUrn(urn);

        fnMap.put(urn, fn);
        if (log.isLoggable(Level.CONFIG)) {
            log.config("BaseFnFactory successfully added to fnMap(urn,fn): " + urn + ", " + fn.getClass().getName());
        }
        if (fn instanceof MatchFn) {
            matchFnMap.put(urn, fn);
            if (log.isLoggable(Level.CONFIG)) {
                log.config("BaseFnFactory successfully added to matchFnMap(urn,fn): " + urn + ", " + fn.getClass().getName());
            }
        }
    }

    public Fn addFn(String urn, ClassLoader cl, String className) throws XacmlException {
        Fn fn = loadInstance(cl, className);
        addFn(urn, fn);
        return fn;
    }

    public Fn addFn(String urn, String className) throws XacmlException {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        Fn fn = loadInstance(cl, className);
        addFn(urn, fn);
        return fn;
    }

    public void addFn(String urn, ClassLoader cl, String className, Class dataType) throws XacmlException {
        Fn fn = loadInstance(cl, className);
        fn.setDataTypes(new Class[]{dataType});
        addFn(urn, fn);
    }

    public void addFn(String urn, ClassLoader cl, String className, Class dataType, Class dataType1) throws XacmlException {
        Fn fn = loadInstance(cl, className);
        fn.setDataTypes(new Class[]{dataType, dataType1});
        addFn(urn, fn);
    }

    public void addFn(String urn, ClassLoader cl, String className, Class dataType, Class dataType1
            , Class dataType2) throws XacmlException {
        Fn fn = loadInstance(cl, className);
        fn.setDataTypes(new Class[]{dataType, dataType1, dataType2});
        addFn(urn, fn);
    }

    public void addFn(String urn, ClassLoader cl, String className, Class[] dataTypes) throws XacmlException {
        Fn fn = loadInstance(cl, className);
        fn.setDataTypes(dataTypes);
        addFn(urn, fn);
    }

    public void setDefaults() throws XacmlException {
        if (log.isLoggable(Level.CONFIG)) {
            log.config("BaseFnFactory configuring default functions...");
        }

        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        try {

// axescon.xacml 2.0 spec, ch. A.3.1 Equality predicates
            addFn(Fn.ANYURI_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", AnyURIAttribute.class);
            addFn(Fn.STRING_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", StringAttribute.class);
            addFn(Fn.INTEGER_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", IntegerAttribute.class);
            addFn(Fn.DOUBLE_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", DoubleAttribute.class);
            addFn(Fn.DATE_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", DateAttribute.class);
            addFn(Fn.DATETIME_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", DateTimeAttribute.class);
            addFn(Fn.TIME_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", TimeAttribute.class);
            addFn(Fn.X500NAME_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", X500NameAttribute.class);
            addFn(Fn.BOOLEAN_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", BooleanAttribute.class);
            addFn(Fn.RFC822NAME_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", RFC822NameAttribute.class);
            addFn(Fn.HEXBINARY_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", HexBinaryAttribute.class);
            addFn(Fn.BASE64BINARY_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", Base64BinaryAttribute.class);
            addFn(Fn.DAYTIMEDURATION_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", DayTimeDurationAttribute.class);
            addFn(Fn.YEARMONTHDURATION_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnEqualBase", YearMonthDurationAttribute.class);

// axescon.xacml 2.0 spec, ch. A.3.2 Arithmetic functions
            addFn(Fn.INTEGER_SUBTRACT,
                    cl, "axescon.xacml.engine.fn.FnArithmeticSubtract", IntegerAttribute.class);
            addFn(Fn.DOUBLE_SUBTRACT,
                    cl, "axescon.xacml.engine.fn.FnArithmeticSubtract", DoubleAttribute.class);
            addFn(Fn.INTEGER_DIVIDE,
                    cl, "axescon.xacml.engine.fn.FnArithmeticDivide", IntegerAttribute.class);
            addFn(Fn.DOUBLE_DIVIDE,
                    cl, "axescon.xacml.engine.fn.FnArithmeticDivide", DoubleAttribute.class);
            addFn(Fn.INTEGER_MULTIPLY,
                    cl, "axescon.xacml.engine.fn.FnArithmeticMultiply", IntegerAttribute.class);
            addFn(Fn.DOUBLE_MULTIPLY,
                    cl, "axescon.xacml.engine.fn.FnArithmeticMultiply", DoubleAttribute.class);
            addFn(Fn.INTEGER_ADD,
                    cl, "axescon.xacml.engine.fn.FnArithmeticAdd", IntegerAttribute.class);
            addFn(Fn.DOUBLE_ADD,
                    cl, "axescon.xacml.engine.fn.FnArithmeticAdd", DoubleAttribute.class);
            addFn(Fn.INTEGER_MOD,
                    cl, "axescon.xacml.engine.fn.FnArithmeticMod", IntegerAttribute.class);
            addFn(Fn.DOUBLE_MOD,
                    cl, "axescon.xacml.engine.fn.FnArithmeticMod", DoubleAttribute.class);
            addFn(Fn.DOUBLE_ABS,
                    cl, "axescon.xacml.engine.fn.FnArithmeticDoubleAbs", DoubleAttribute.class);
            addFn(Fn.INTEGER_ABS,
                    cl, "axescon.xacml.engine.fn.FnArithmeticIntegerAbs", IntegerAttribute.class);
            addFn(Fn.FLOOR,
                    cl, "axescon.xacml.engine.fn.FnArithmeticFloor", DoubleAttribute.class);
            addFn(Fn.ROUND,
                    cl, "axescon.xacml.engine.fn.FnArithmeticRound", DoubleAttribute.class);

// axescon.xacml 2.0 spec, ch. A.3.3 String conversion functions
            addFn(Fn.STRING_NORMALIZE_SPACE,
                    cl, "axescon.xacml.engine.fn.FnStringNormalizeSpace", StringAttribute.class);
            addFn(Fn.STRING_NORMALIZE_TO_LOWER_CASE,
                    cl, "axescon.xacml.engine.fn.FnStringNormalizeToLowerCase", StringAttribute.class);

// axescon.xacml 2.0 spec, ch. A.3.4 Numeric data-type conversion functions
            addFn(Fn.DOUBLE_TO_INTEGER,
                    cl, "axescon.xacml.engine.fn.FnNumericConversionDoubleToInteger", DoubleAttribute.class);
            addFn(Fn.INTEGER_TO_DOUBLE,
                    cl, "axescon.xacml.engine.fn.FnNumericConversionIntegerToDouble", IntegerAttribute.class);

// axescon.xacml 2.0 spec, ch. A.3.5 Logical functions
            addFn(Fn.OR,
                    cl, "axescon.xacml.engine.fn.FnLogicalOr");
            addFn(Fn.AND,
                    cl, "axescon.xacml.engine.fn.FnLogicalAnd");
            addFn(Fn.NOT,
                    cl, "axescon.xacml.engine.fn.FnLogicalNot");
            addFn(Fn.N_OF,
                    cl, "axescon.xacml.engine.fn.FnLogicalNOf");

            // axescon.xacml 2.0 spec, ch. A.3.6 Numeric comparison functions
            addFn(Fn.INTEGER_GREATER_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThanOrEqual", IntegerAttribute.class);
            addFn(Fn.INTEGER_GREATER_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThan", IntegerAttribute.class);
            addFn(Fn.INTEGER_LESS_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThan", IntegerAttribute.class);
            addFn(Fn.INTEGER_LESS_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThanOrEqual", IntegerAttribute.class);


            addFn(Fn.DOUBLE_GREATER_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThanOrEqual", DoubleAttribute.class);
            addFn(Fn.DOUBLE_GREATER_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThan", DoubleAttribute.class);
            addFn(Fn.DOUBLE_LESS_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThan", DoubleAttribute.class);
            addFn(Fn.DOUBLE_LESS_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThanOrEqual", DoubleAttribute.class);

// axescon.xacml 2.0 spec, ch. A.3.7 Date and time arithmetic functions
            addFn(Fn.DATETIME_ADD_DAYTIMEDURATION,
                    cl, "axescon.xacml.engine.fn.FnDTArithmeticDateTimeAddDayTimeDuration"
                    , DateTimeAttribute.class, DayTimeDurationAttribute.class);
            addFn(Fn.DATETIME_SUBTRACT_DAYTIMEDURATION,
                    cl, "axescon.xacml.engine.fn.FnDTArithmeticDateTimeSubtractDayTimeDuration"
                    , DateTimeAttribute.class, DayTimeDurationAttribute.class);
            addFn(Fn.DATETIME_ADD_YEARMONTHDURATION,
                    cl, "axescon.xacml.engine.fn.FnDTArithmeticDateTimeAddYearMonthDuration"
                    , DateTimeAttribute.class, YearMonthDurationAttribute.class);
            addFn(Fn.DATETIME_SUBTRACT_YEARMONTHDURATION,
                    cl, "axescon.xacml.engine.fn.FnDTArithmeticDateTimeSubtractYearMonthDuration"
                    , DateTimeAttribute.class, YearMonthDurationAttribute.class);
            addFn(Fn.DATE_ADD_YEARMONTHDURATION,
                    cl, "axescon.xacml.engine.fn.FnDTArithmeticDateAddYearMonthDuration"
                    , DateAttribute.class, YearMonthDurationAttribute.class);
            addFn(Fn.DATE_SUBTRACT_YEARMONTHDURATION,
                    cl, "axescon.xacml.engine.fn.FnDTArithmeticDateSubtractYearMonthDuration"
                    , DateAttribute.class, YearMonthDurationAttribute.class);

// axescon.xacml 2.0 spec, ch. A.3.8 Non-numeric comparison functions
            addFn(Fn.STRING_GREATER_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThanOrEqual", StringAttribute.class);
            addFn(Fn.STRING_GREATER_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThan", StringAttribute.class);
            addFn(Fn.STRING_LESS_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThan", StringAttribute.class);
            addFn(Fn.STRING_LESS_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThanOrEqual", StringAttribute.class);

            addFn(Fn.ANYURI_GREATER_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThanOrEqual", AnyURIAttribute.class);
            addFn(Fn.ANYURI_GREATER_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThan", AnyURIAttribute.class);
            addFn(Fn.ANYURI_LESS_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThan", AnyURIAttribute.class);
            addFn(Fn.ANYURI_LESS_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThanOrEqual", AnyURIAttribute.class);

            addFn(Fn.TIME_GREATER_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThanOrEqual", TimeAttribute.class);
            addFn(Fn.TIME_GREATER_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThan", TimeAttribute.class);
            addFn(Fn.TIME_LESS_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThan", TimeAttribute.class);
            addFn(Fn.TIME_LESS_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThanOrEqual", TimeAttribute.class);

            addFn(Fn.TIME_IN_RANGE,
                    cl, "axescon.xacml.engine.fn.FnTimeInRange");

            addFn(Fn.DATE_GREATER_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThanOrEqual", DateAttribute.class);
            addFn(Fn.DATE_GREATER_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThan", DateAttribute.class);
            addFn(Fn.DATE_LESS_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThan", DateAttribute.class);
            addFn(Fn.DATE_LESS_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThanOrEqual", DateAttribute.class);

            addFn(Fn.DATETIME_GREATER_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThanOrEqual", DateTimeAttribute.class);
            addFn(Fn.DATETIME_GREATER_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThan", DateTimeAttribute.class);
            addFn(Fn.DATETIME_LESS_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThan", DateTimeAttribute.class);
            addFn(Fn.DATETIME_LESS_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThanOrEqual", DateTimeAttribute.class);

            addFn(Fn.X500NAME_GREATER_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThanOrEqual"
                    , X500NameAttribute.class);
            addFn(Fn.X500NAME_GREATER_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseGreaterThan"
                    , X500NameAttribute.class);
            addFn(Fn.X500NAME_LESS_THAN,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThan"
                    , X500NameAttribute.class);
            addFn(Fn.X500NAME_LESS_THAN_OR_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnComparisonBaseLessThanOrEqual"
                    , X500NameAttribute.class);

            // axescon.xacml 2.0 spec, ch. A.3.9 String functions
//todo missing! string-concatenate and url-string-concatenate
            addFn(Fn.STRING_CONCATENATE,
                    cl, "axescon.xacml.engine.fn.FnStringConcatenate");
            addFn(Fn.URL_STRING_CONCATENATE,
                    cl, "axescon.xacml.engine.fn.FnUrlStringConcatenate");

            // axescon.xacml 2.0 spec, ch. A.3.10 Bag functions
            addFn(Fn.STRING_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", StringAttribute.class);
            addFn(Fn.ANYURI_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", AnyURIAttribute.class);
            addFn(Fn.DNSNAME_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", DnsNameAttribute.class);
            addFn(Fn.IPADDRESS_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", IpAddressAttribute.class);
            addFn(Fn.RFC822NAME_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", RFC822NameAttribute.class);
            addFn(Fn.X500NAME_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", X500NameAttribute.class);
            addFn(Fn.INTEGER_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", IntegerAttribute.class);
            addFn(Fn.DOUBLE_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", DoubleAttribute.class);
            addFn(Fn.TIME_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", TimeAttribute.class);
            addFn(Fn.DATETIME_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", DateTimeAttribute.class);
            addFn(Fn.DATE_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", DateAttribute.class);
            addFn(Fn.HEXBINARY_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", HexBinaryAttribute.class);
            addFn(Fn.BASE64BINARY_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", Base64BinaryAttribute.class);
            addFn(Fn.BOOLEAN_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", BooleanAttribute.class);
            addFn(Fn.DAYTIMEDURATION_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", DayTimeDurationAttribute.class);
            addFn(Fn.YEARMONTHDURATION_IS_IN,
                    cl, "axescon.xacml.engine.fn.FnBagBaseIsIn", YearMonthDurationAttribute.class);

            addFn(Fn.STRING_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", StringAttribute[].class);
            addFn(Fn.BASE64BINARY_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", Base64BinaryAttribute[].class);
            addFn(Fn.ANYURI_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", AnyURIAttribute[].class);
            addFn(Fn.DNSNAME_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", DnsNameAttribute[].class);
            addFn(Fn.IPADDRESS_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", IpAddressAttribute[].class);
            addFn(Fn.RFC822NAME_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", RFC822NameAttribute[].class);
            addFn(Fn.X500NAME_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", X500NameAttribute[].class);
            addFn(Fn.INTEGER_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", IntegerAttribute[].class);
            addFn(Fn.DOUBLE_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", DoubleAttribute[].class);
            addFn(Fn.TIME_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", TimeAttribute[].class);
            addFn(Fn.DATETIME_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", DateTimeAttribute[].class);
            addFn(Fn.DATE_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", DateAttribute[].class);
            addFn(Fn.HEXBINARY_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", HexBinaryAttribute[].class);
            addFn(Fn.BASE64BINARY_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", Base64BinaryAttribute[].class);
            addFn(Fn.BOOLEAN_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", BooleanAttribute[].class);
            addFn(Fn.DAYTIMEDURATION_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", DayTimeDurationAttribute[].class);
            addFn(Fn.YEARMONTHDURATION_ONE_AND_ONLY,
                    cl, "axescon.xacml.engine.fn.FnBagOneAndOnly", YearMonthDurationAttribute[].class);

            addFn(Fn.STRING_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", StringAttribute[].class);
            addFn(Fn.ANYURI_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", AnyURIAttribute[].class);
            addFn(Fn.DNSNAME_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", DnsNameAttribute[].class);
            addFn(Fn.IPADDRESS_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", IpAddressAttribute[].class);
            addFn(Fn.RFC822NAME_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", RFC822NameAttribute[].class);
            addFn(Fn.X500NAME_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", X500NameAttribute[].class);
            addFn(Fn.INTEGER_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", IntegerAttribute[].class);
            addFn(Fn.DOUBLE_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", DoubleAttribute[].class);
            addFn(Fn.TIME_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", TimeAttribute[].class);
            addFn(Fn.DATETIME_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", DateTimeAttribute[].class);
            addFn(Fn.DATE_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", DateAttribute[].class);
            addFn(Fn.BOOLEAN_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", BooleanAttribute[].class);
            addFn(Fn.HEXBINARY_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", HexBinaryAttribute[].class);
            addFn(Fn.BASE64BINARY_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", Base64BinaryAttribute[].class);
            addFn(Fn.DAYTIMEDURATION_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", DayTimeDurationAttribute[].class);
            addFn(Fn.YEARMONTHDURATION_BAG_SIZE,
                    cl, "axescon.xacml.engine.fn.FnBagSizeOf", YearMonthDurationAttribute[].class);

            addFn(Fn.ANYURI_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", AnyURIAttribute.class);
            addFn(Fn.STRING_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", StringAttribute.class);
            addFn(Fn.INTEGER_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", IntegerAttribute.class);
            addFn(Fn.DOUBLE_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", DoubleAttribute.class);
            addFn(Fn.DATE_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", DateAttribute.class);
            addFn(Fn.DATETIME_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", DateTimeAttribute.class);
            addFn(Fn.TIME_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", TimeAttribute.class);
            addFn(Fn.X500NAME_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", X500NameAttribute.class);
            addFn(Fn.BOOLEAN_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", BooleanAttribute.class);
            addFn(Fn.RFC822NAME_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", RFC822NameAttribute.class);
            addFn(Fn.HEXBINARY_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", HexBinaryAttribute.class);
            addFn(Fn.BASE64BINARY_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", Base64BinaryAttribute.class);
            addFn(Fn.DAYTIMEDURATION_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", DayTimeDurationAttribute.class);
            addFn(Fn.YEARMONTHDURATION_BAG,
                    cl, "axescon.xacml.engine.fn.FnBag", YearMonthDurationAttribute.class);

            // axescon.xacml 2.0 spec, ch.             A.3.11 Set functions
            addFn(Fn.ANYURI_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", AnyURIAttribute[].class);
            addFn(Fn.STRING_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", StringAttribute[].class);
            addFn(Fn.INTEGER_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", IntegerAttribute[].class);
            addFn(Fn.DOUBLE_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", DoubleAttribute[].class);
            addFn(Fn.DATE_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", DateAttribute[].class);
            addFn(Fn.DATETIME_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", DateTimeAttribute[].class);
            addFn(Fn.TIME_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", TimeAttribute[].class);
            addFn(Fn.X500NAME_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", X500NameAttribute[].class);
            addFn(Fn.BOOLEAN_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", BooleanAttribute[].class);
            addFn(Fn.RFC822NAME_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", RFC822NameAttribute[].class);
            addFn(Fn.HEXBINARY_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", HexBinaryAttribute[].class);
            addFn(Fn.BASE64BINARY_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", Base64BinaryAttribute[].class);
            addFn(Fn.DAYTIMEDURATION_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", DayTimeDurationAttribute[].class);
            addFn(Fn.YEARMONTHDURATION_INTERSECTION,
                    cl, "axescon.xacml.engine.fn.FnSetIntersectionBase", YearMonthDurationAttribute[].class);

            addFn(Fn.ANYURI_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", AnyURIAttribute[].class);
            addFn(Fn.STRING_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", StringAttribute[].class);
            addFn(Fn.INTEGER_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", IntegerAttribute[].class);
            addFn(Fn.DOUBLE_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", DoubleAttribute[].class);
            addFn(Fn.DATE_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", DateAttribute[].class);
            addFn(Fn.DATETIME_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", DateTimeAttribute[].class);
            addFn(Fn.TIME_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", TimeAttribute[].class);
            addFn(Fn.X500NAME_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", X500NameAttribute[].class);
            addFn(Fn.BOOLEAN_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", BooleanAttribute[].class);
            addFn(Fn.RFC822NAME_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", RFC822NameAttribute[].class);
            addFn(Fn.HEXBINARY_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", HexBinaryAttribute[].class);
            addFn(Fn.BASE64BINARY_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", Base64BinaryAttribute[].class);
            addFn(Fn.DAYTIMEDURATION_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", DayTimeDurationAttribute[].class);
            addFn(Fn.YEARMONTHDURATION_SUBSET,
                    cl, "axescon.xacml.engine.fn.FnSetSubsetBase", YearMonthDurationAttribute[].class);

            addFn(Fn.ANYURI_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", AnyURIAttribute[].class);
            addFn(Fn.STRING_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", StringAttribute[].class);
            addFn(Fn.INTEGER_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", IntegerAttribute[].class);
            addFn(Fn.DOUBLE_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", DoubleAttribute[].class);
            addFn(Fn.DATE_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", DateAttribute[].class);
            addFn(Fn.DATETIME_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", DateTimeAttribute[].class);
            addFn(Fn.TIME_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", TimeAttribute[].class);
            addFn(Fn.X500NAME_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", X500NameAttribute[].class);
            addFn(Fn.BOOLEAN_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", BooleanAttribute[].class);
            addFn(Fn.RFC822NAME_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", RFC822NameAttribute[].class);
            addFn(Fn.HEXBINARY_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", HexBinaryAttribute[].class);
            addFn(Fn.BASE64BINARY_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", Base64BinaryAttribute[].class);
            addFn(Fn.DAYTIMEDURATION_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", DayTimeDurationAttribute[].class);
            addFn(Fn.YEARMONTHDURATION_UNION,
                    cl, "axescon.xacml.engine.fn.FnSetUnionBase", YearMonthDurationAttribute[].class);

            addFn(Fn.ANYURI_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", AnyURIAttribute[].class);
            addFn(Fn.STRING_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", StringAttribute[].class);
            addFn(Fn.INTEGER_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", IntegerAttribute[].class);
            addFn(Fn.DOUBLE_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", DoubleAttribute[].class);
            addFn(Fn.DATE_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", DateAttribute[].class);
            addFn(Fn.DATETIME_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", DateTimeAttribute[].class);
            addFn(Fn.TIME_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", TimeAttribute[].class);
            addFn(Fn.X500NAME_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", X500NameAttribute[].class);
            addFn(Fn.BOOLEAN_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", BooleanAttribute[].class);
            addFn(Fn.RFC822NAME_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", RFC822NameAttribute[].class);
            addFn(Fn.HEXBINARY_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", HexBinaryAttribute[].class);
            addFn(Fn.BASE64BINARY_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", Base64BinaryAttribute[].class);
            addFn(Fn.DAYTIMEDURATION_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", DayTimeDurationAttribute[].class);
            addFn(Fn.YEARMONTHDURATION_SET_EQUALS,
                    cl, "axescon.xacml.engine.fn.FnSetEqualsBase", YearMonthDurationAttribute[].class);

            addFn(Fn.ANYURI_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", AnyURIAttribute[].class);
            addFn(Fn.STRING_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", StringAttribute[].class);
            addFn(Fn.INTEGER_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", IntegerAttribute[].class);
            addFn(Fn.DOUBLE_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", DoubleAttribute[].class);
            addFn(Fn.DATE_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", DateAttribute[].class);
            addFn(Fn.DATETIME_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", DateTimeAttribute[].class);
            addFn(Fn.TIME_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", TimeAttribute[].class);
            addFn(Fn.X500NAME_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", X500NameAttribute[].class);
            addFn(Fn.BOOLEAN_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", BooleanAttribute[].class);
            addFn(Fn.RFC822NAME_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", RFC822NameAttribute[].class);
            addFn(Fn.HEXBINARY_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", HexBinaryAttribute[].class);
            addFn(Fn.BASE64BINARY_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", Base64BinaryAttribute[].class);
            addFn(Fn.DAYTIMEDURATION_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", DayTimeDurationAttribute[].class);
            addFn(Fn.YEARMONTHDURATION_AT_LEAST_ONE_MEMBER_OF,
                    cl, "axescon.xacml.engine.fn.FnSetAtLeastOneMemberOfBase", YearMonthDurationAttribute[].class);

            // axescon.xacml 2.0 spec, ch.           A.3.12 Higher-order bag functions
            addFn(Fn.ANY_OF,
                    cl, "axescon.xacml.engine.fn.FnHOBagAnyOfBase"
                    , StringAttribute.class, AttributeValue.class, AttributeValue[].class);
            addFn(Fn.ALL_OF,
                    cl, "axescon.xacml.engine.fn.FnHOBagAllOfBase"
                    , StringAttribute.class, AttributeValue.class, AttributeValue[].class);
            addFn(Fn.ALL_OF_ALL,
                    cl, "axescon.xacml.engine.fn.FnHOBagAllOfAllBase"
                    , StringAttribute.class, AttributeValue[].class, AttributeValue[].class);
            addFn(Fn.ALL_OF_ANY,
                    cl, "axescon.xacml.engine.fn.FnHOBagAllOfAnyBase"
                    , StringAttribute.class, AttributeValue[].class, AttributeValue[].class);
            addFn(Fn.ANY_OF_ALL,
                    cl, "axescon.xacml.engine.fn.FnHOBagAnyOfAllBase"
                    , StringAttribute.class, AttributeValue[].class, AttributeValue[].class);
            addFn(Fn.ANY_OF_ANY,
                    cl, "axescon.xacml.engine.fn.FnHOBagAnyOfAnyBase"
                    , StringAttribute.class, AttributeValue[].class, AttributeValue[].class);
            addFn(Fn.MAP,
                    cl, "axescon.xacml.engine.fn.FnHOBagMap"
                    , StringAttribute.class, AttributeValue[].class);

// axescon.xacml 2.0 spec, ch.   A.3.13 Regular-expression-based 4818 functions
            addFn(Fn.STRING_REGEXP_MATCH,
                    cl, "axescon.xacml.engine.fn.FnBaseMatch"
                    , StringAttribute.class, StringAttribute.class);
            addFn(Fn.ANYURI_REGEXP_MATCH,
                    cl, "axescon.xacml.engine.fn.FnBaseMatch"
                    , StringAttribute.class, AnyURIAttribute.class);
            addFn(Fn.DNSNAME_REGEXP_MATCH,
                    cl, "axescon.xacml.engine.fn.FnBaseMatch"
                    , StringAttribute.class, DnsNameAttribute.class);
            addFn(Fn.IPADDRESS_REGEXP_MATCH,
                    cl, "axescon.xacml.engine.fn.FnBaseMatch"
                    , StringAttribute.class, IpAddressAttribute.class);
            addFn(Fn.RFC822NAME_REGEXP_MATCH,
                    cl, "axescon.xacml.engine.fn.FnBaseMatch"
                    , StringAttribute.class, RFC822NameAttribute.class);
            addFn(Fn.X500NAME_REGEXP_MATCH,
                    cl, "axescon.xacml.engine.fn.FnBaseMatch"
                    , StringAttribute.class, X500NameAttribute.class);

// axescon.xacml 2.0 spec, ch.  A.3.14 Special match functions
            Fn fn = loadInstance(cl, "axescon.xacml.engine.fn.FnX500NameMatch");
            fn.setDataTypes(new Class[]{X500NameAttribute.class, X500NameAttribute.class});
            addFn(Fn.X500NAME_MATCH,
                    fn);

            fn = loadInstance(cl, "axescon.xacml.engine.fn.FnRfc822NameMatch");
            fn.setDataTypes(new Class[]{StringAttribute.class, RFC822NameAttribute.class});
            addFn(Fn.RFC822NAME_MATCH,
                    fn);

// axescon.xacml 2.0 spec, ch.  A.3.15 XPath-based functions
            addFn(Fn.XPATH_NODE_COUNT,
                    cl, "axescon.xacml.engine.fn.FnXpathNodeCount"
                    , StringAttribute.class);
            addFn(Fn.XPATH_NODE_EQUAL,
                    cl, "axescon.xacml.engine.fn.FnXpathNodeEqual"
                    , StringAttribute.class, StringAttribute.class);
            addFn(Fn.XPATH_NODE_MATCH,
                    cl, "axescon.xacml.engine.fn.FnXpathNodeMatch"
                    , StringAttribute.class, StringAttribute.class);

            if (log.isLoggable(Level.CONFIG)) {
                log.config("BaseFnFactory completed configuring default functions.");
            }


        } catch (Exception e) {
            if (log.isLoggable(Level.SEVERE)) {
                log.severe("BaseFnFactory configuring default functions FAILED.");
            }
            // all standard classes should load
            throw new XacmlException("BaseFnFactory cant instatiate function class", e
                    , XacmlException.ERR_CODE_CLASS_INSTANTIATE);
        }


    }

    public MatchFn getMatchFn(String fnName) throws UnsupportedFunctionException {
        MatchFn fn = (MatchFn) matchFnMap.get(fnName);
        if (fn == null) throw new UnsupportedFunctionException("BaseFnFactory.getMatchFn(" + fnName);
        return fn;
    }


    public boolean match(String fnName, AttributeValue arg0, AttributeValue arg1) throws
            UnsupportedFunctionException, ExpressionEvalIndeterminateException {
        return getMatchFn(fnName).match(arg0, arg1);
    }


    /**
     * @return Map<String, Fn>
     */
    public Map getFnMap() {
        return fnMap;
    }

    /**
     * @param fnMap Map<String, Fn>
     */
    public void setFnMap(Map fnMap) {
        setFactory(fnMap);
        this.fnMap = fnMap;
    }

    /**
     * @return Map<String, MatchFn>
     */
    public Map getMatchFnMap() {
        return matchFnMap;
    }

    /**
     * @param matchFnMap Map<String, MatchFn>
     */
    public void setMatchFnMap(Map matchFnMap) {
        setFactory(matchFnMap);
        this.matchFnMap = matchFnMap;
    }

    public void setFactory(Map fnMap) {
        Iterator it = fnMap.values().iterator();
        while (it.hasNext()) {
            ((Fn) it.next()).setFnFactory(this);
        }
    }


    public Fn getFn(String fnName) throws UnsupportedFunctionException {
        Fn fn = (Fn) fnMap.get(fnName);
        if (fn == null) throw new UnsupportedFunctionException("BaseFnFactory.getFn(" + fnName);
        return fn;
    }

    /**
     * this method validates function set
     *
     * @throws XacmlException if syntax error is found
     */
    public void validate() throws XacmlException {

        Iterator it = fnMap.values().iterator();
        while (it.hasNext()) {
            Fn f = (Fn) it.next();
            f.getReturnDataType();//check that no exception
        }

        for (int i = 0; i < Fn.mandatoryFns.length; i++) {

            if (!fnMap.containsKey(Fn.mandatoryFns[i])) {
                log.severe("Not loaded mandatory Fn urn:" + Fn.mandatoryFns[i]);
            }

        }
        for (int i = 0; i < Fn.optionalFns.length; i++) {


            if (!fnMap.containsKey(Fn.optionalFns[i])) {
                log.warning("Not loaded optional Fn urn:" + Fn.optionalFns[i]);
            }
        }
    }


}
